package math.vectorSpace.R3;

import java.awt.Graphics2D;
import java.awt.Polygon;
import math.vectorSpace.R2.Point2D;
import math.vectorSpace.R3.visualization.Visualizable;
import math.vectorSpace.R3.visualization.Render3D;
import math.vectorSpace.R3.visualization.VisualizationProperties;

public class Plane3D implements Visualizable {

    private Vector3D normal;

    private Point3D refPoint;

    private double D;

    /**
     * Constructs a plane from its normal and a point on the plane
     * 
     * @param point
     * @param normal
     */
    public Plane3D(Point3D point, Vector3D normal) {
        this.normal = normal;
        refPoint = point;
        D = -(normal.getX() * point.getX() + normal.getY() * point.getY() + normal.getZ() * point.getZ());
    }

    /**
     * Constructs a plane from two direction vectors and a point that lie on the plane.
     * Fails if the two vectors are colinear.
     * 
     * @param v1
     * @param v2
     * @param point
     */
    public Plane3D(Vector3D v1, Vector3D v2, Point3D point) {
        this(point, v1.cross(v2));
    }

    public Plane3D(Vector3D v1, Vector3D v2) {
        this(new Point3D(), v1.cross(v2));
    }

    public Plane3D(Vector3D normal) {
        this(new Point3D(), normal);
    }

    /*
    public Plane3D(double A, double B, double C, double D) {
    this (new Vector3D(A, B, C), );
    }
    
     */
    /**
     * Creates a plane through the origin with the given normal 
     * vector components
     * 
     * @param A
     * @param B
     * @param C 
     */
    public Plane3D(double A, double B, double C) {
        refPoint = new Point3D();
        normal = new Vector3D(A, B, C);
        this.D = 0.0;
    }

    /**
     * Creates a plane with the specified reference point coordinates
     * and normal vector components
     * 
     * @param rX
     * @param rY 
     * @param rZ 
     * @param nY
     * @param nX  
     */
    public Plane3D(double rX, double rY, double rZ, double nX, double nY,
            double nZ) {
        this(new Point3D(rX, rY, rZ), new Vector3D(nX, nY, nZ));
    }

    public double getA() {
        return normal.getX();
    }

    public double getB() {
        return normal.getY();
    }

    public double getC() {
        return normal.getZ();
    }

    public Vector3D getNormal() {
        return normal;
    }

    public Point3D getRefPoint() {
        return refPoint;
    }

    public double getD() {
        return D;
    }

    public void setNormal(Vector3D vector) {
        normal = vector;
    }

    public void setRefPoint(Point3D point) {
        refPoint = point;
        D = -(normal.getX() * point.getX() + normal.getY() * point.getY() + normal.getZ() * point.getZ());
    }

    /*
    public void setD(double D) {
    this.D = D;
    }
     */
    /**
     * Projects a given segment onto the surface of this plane
     * @param segment
     * @return
     */
    public Segment3D project(Segment3D segment) {
        // Projects the start and end point of the segment onto the plane
        // Creates a new segment using the two projected points
        Point3D point1 = segment.getStart().project(this);
        Point3D point2 = segment.getEnd().project(this);
        return (new Segment3D(point1, point2));
    }

    @Override
    public String toString() {
        return ("Plane3D: " + normal.getX() + "x + " + normal.getY() + "y + "
                + normal.getZ() + "z + " + D + " = 0");
    }

    private void add(Polygon rectangle, Point2D point) {
        rectangle.addPoint((int) point.getX(), (int) point.getY());
    }

    /**
     * 
     * @param render
     * @param graphics
     * @param properties  
     */
    @Override
    public void draw(Render3D render, Graphics2D graphics,
            VisualizationProperties properties) {
        Point3D center = new Point3D().project(this);
        Vector3D dirVector1 = new Vector3D(1.0, 0, 0).cross(
                this.normal).scaleToSize(1000);
        Vector3D dirVector2 = dirVector1.cross(
                this.normal).scaleToSize(1000);
        Polygon rectangle = new Polygon();
        add(rectangle, render.project(center.add(dirVector1)));
        add(rectangle, render.project(center.add(dirVector2)));
        add(rectangle, render.project(center.subtract(dirVector1)));
        add(rectangle, render.project(center.subtract(dirVector2)));

        graphics.setColor(properties.getFillColor());
        graphics.fill(rectangle);
    }
}
